using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using Castle.DynamicProxy;
using Castle.Core.Interceptor;
using System.Collections.ObjectModel;
using System.Runtime.Serialization;
using System.Globalization;
using System.Reflection;
using Legend.Fakes.Configuration;

namespace Legend.Fakes.Api
{
    /// <summary>
    /// Keeps track of metadata for interceptions.
    /// </summary>
    [Serializable]
    internal class CallRuleMetadata
    {
        public IFakeObjectCallRule Rule;
        public int CalledNumberOfTimes;

        public bool HasNotBeenUsed()
        {
            return this.CalledNumberOfTimes == 0;
        }

        public bool HasNotBeenCalledSpecifiedNumberOfTimes()
        {
            return this.Rule.NumberOfTimesToCall != null && this.CalledNumberOfTimes < this.Rule.NumberOfTimesToCall.Value;
        }

        public void Verify()
        {
            this.VerifyThatRuleHasNotBeenCalledTooManyTimes();
            this.VerifyThatRuleHasNotBeenCalledTooFewTimes();
        }

        private void VerifyThatRuleHasNotBeenCalledTooManyTimes()
        {
            if (this.Rule.MayNotBeCalledMoreThanTheNumberOfTimesSpecified && this.Rule.NumberOfTimesToCall < this.CalledNumberOfTimes)
            {
                throw this.GetExceptionForCalledTooManyTimes();
            }
        }

        private void VerifyThatRuleHasNotBeenCalledTooFewTimes()
        {
            if (this.Rule.MustGetCalled && this.Rule.NumberOfTimesToCall > this.CalledNumberOfTimes)
            {
                throw this.GetExceptionForCalledTooFewTimes();
            }
        }

        private Exception GetExceptionForCalledTooFewTimes()
        {
            var messageTemplate = ExceptionMessages.CalledTooFewTimesMessage;

            return new ExpectationException(string.Format(CultureInfo.CurrentCulture,
                messageTemplate, this.Rule.ToString(), this.Rule.NumberOfTimesToCall, this.CalledNumberOfTimes));
        }

        private Exception GetExceptionForCalledTooManyTimes()
        {
            var messageTemplate = ExceptionMessages.CalledTooManyTimesMessage;

            return new ExpectationException(string.Format(CultureInfo.CurrentCulture,
                messageTemplate, this.Rule.ToString(), this.Rule.NumberOfTimesToCall, this.CalledNumberOfTimes));
        }
    }
}
